/*
 * @Author: 吴世扬 18368095041@163.com
 * @Date: 2025-02-21 16:26:13
 * @LastEditors: 吴世扬 18368095041@163.com
 * @LastEditTime: 2025-02-27 15:57:21
 * @FilePath: /vue3-admin-server/src/services/serveAssetsService.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import * as fsModule from 'fs';
import * as pathModule from 'path';
import { InjectRepository } from '@nestjs/typeorm';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { createHash } from 'crypto';

import { UploadFileDTO } from 'src/common/dto';
import { STATIC_DIR, STATIC_BUCKET } from 'src/common/constants';
import { FileEntity } from 'src/entities/file.entity';
import {
    generateRandomKey,
    generateRandomIV,
    aesEncrypt,
    aesDecrypt,
    isObject
} from 'src/common/utils';

const key = generateRandomKey();
const iv = generateRandomIV();
const ivHex = iv.toString('hex');

@Injectable()
export class FileService {
    // readFile(url: string) {
    //     return;
    // }
    constructor(
        @InjectRepository(FileEntity)
        private readonly fileRepository: Repository<FileEntity>
    ) {}

    async uploadFile(
        file: Express.Multer.File,
        uploadFileDTO: UploadFileDTO,
        uploaderId?: number
    ) {
        try {
            // 从环境变量获取配置路径
            const basePath = process.env.STATIC_DIR ?? STATIC_DIR;
            const bucket =
                uploadFileDTO.bucket && uploadFileDTO.bucket !== ''
                    ? uploadFileDTO.bucket
                    : STATIC_BUCKET;
            // 生成唯一文件名
            const timestamp = Date.now().toString();
            const uniqueFileName = `${timestamp}-${file.originalname}`;
            const fileName = createHash('md5')
                .update(uniqueFileName)
                .digest('hex');

            // 生成完整路径
            const filePath = pathModule.join(
                process.cwd(),
                basePath,
                bucket,
                fileName
            );

            const dir = pathModule.dirname(filePath);

            // 确保目录存在
            fsModule.mkdirSync(dir, { recursive: true });

            // 写入文件
            fsModule.writeFileSync(filePath, file.buffer);

            const fileEntity = new FileEntity();
            fileEntity.bucket = bucket;
            fileEntity.path = pathModule.join(basePath, bucket, fileName);
            fileEntity.type = uploadFileDTO.type;
            fileEntity.name = file.originalname;
            fileEntity.remark = uploadFileDTO.remark ?? '';
            fileEntity.uploaderId = uploaderId;
            const queryBuilder = this.fileRepository.createQueryBuilder();
            const { identifiers } = await queryBuilder
                .insert()
                .into(FileEntity)
                .values(fileEntity)
                .execute();
            return {
                id: identifiers[0].id as number,
                token: this.encryptFile(fileEntity)
            };
        } catch (e) {
            return Promise.reject(e as Error);
        }
    }

    async readFile(token: string) {
        try {
            const { path, type } = this.decryptFile(token);
            const filePath = pathModule.join(process.cwd(), path);
            const data = await new Promise<Buffer<ArrayBufferLike>>(
                (resolve, reject) => {
                    fsModule.readFile(filePath, (err, data) => {
                        if (err) {
                            console.error(err);
                            reject(err);
                            return;
                        }
                        resolve(data);
                    });
                }
            );
            return {
                buffer: data,
                type
            };
        } catch (e) {
            return Promise.reject(e as Error);
        }
    }

    // 添加加密方法
    encryptFile(file: FileEntity): string {
        const obj: VO.BaseFileVO = {
            id: file.id,
            bucket: file.bucket,
            path: file.path,
            type: file.type,
            name: file.name
        };
        const json = JSON.stringify(obj);
        const { encryptedData } = aesEncrypt(json, key, iv);
        return encryptedData;
    }

    decryptFile(encryptedPath: string): VO.BaseFileVO {
        const json = aesDecrypt(encryptedPath, key, ivHex);
        const obj: unknown = JSON.parse(json);
        // 检查解密后的对象是否有效
        if (!isObject(obj)) {
            throw new Error('Invalid decrypted object');
        }
        return obj as unknown as VO.BaseFileVO;
    }
}
